#include<stdio.h>      /*标准输入输出定义*/
#include<stdlib.h>     /*标准函数库定义*/
#include<unistd.h>     /*Unix 标准函数定义*/
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>      /*文件控制定义*/
#include<termios.h>    /*PPSIX 终端控制定义*/
#include<errno.h>      /*错误号定义*/
#include<string.h>
#include<stdlib.h>

//宏定义
#define FALSE  -1
#define TRUE   0

/*******************************************************************
 * 名称：uart_open
 * 功能：打开串口并返回串口设备文件描述
 * 入口参数：fd:文件描述符port :串口号(ttyS0,ttyS1,ttyS2)
 * 出口参数：正确返回为1，错误返回为0
 *******************************************************************/
int uart_open(char* port)
{
	int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

	if (FALSE == fd)
	{
		perror("Can't Open Serial Port");
		return (FALSE);
	}
	//恢复串口为阻塞状态
	if (fcntl(fd, F_SETFL, 0) < 0)
	{
		printf("fcntl failed!\n");
		return (FALSE);
	}
	else
	{
		printf("fcntl=%d\n", fcntl(fd, F_SETFL, 0));
	}
	// 测试是否为终端设备
	if (0 == isatty(STDIN_FILENO))
	{
		printf("standard input is not a terminal device\n");
		return (FALSE);
	}
	else
	{
		printf("isatty success!\n");
	}
	printf("fd->open=%d\n", fd);
	return fd;
}


int uart_open_no_block(char* port)
{
	int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);

	if (FALSE == fd)
	{
		perror("Can't Open Serial Port");
		return (FALSE);
	}
	int x;
	x = fcntl(fd, F_GETFL, 0);
	fcntl(fd, F_SETFL, x | O_NONBLOCK);
	// 恢复串口为阻塞状态
	// if (fcntl(fd, F_SETFL, 0) < 0)
	// {
	// 	printf("fcntl failed!\n");
	// 	return (FALSE);
	// }
	// else
	// {
	// 	printf("fcntl=%d\n", fcntl(fd, F_SETFL, 0));
	// }
	// 测试是否为终端设备
	if (0 == isatty(STDIN_FILENO))
	{
		printf("standard input is not a terminal device\n");
		return (FALSE);
	}
	else
	{
		printf("isatty success!\n");
	}
	printf("fd->open=%d\n", fd);
	return fd;
}

/*******************************************************************
 * 名称：uart_close
 * 功能：关闭串口并返回串口设备文件描述
 * 入口参数：fd:文件描述符port :串口号(ttyS0,ttyS1,ttyS2)
 * 出口参数：void
 *******************************************************************/
void uart_close(int fd)
{
	close(fd);
}

/*******************************************************************
 *名称：uart_set
 *功能：设置串口数据位，停止位和效验位
 *入口参数：fd串口文件描述符
 *speed:串口速度
 *flow_ctrl:数据流控制
 *databits:数据位   取值为 7 或者8
 *stopbits:停止位   取值为 1 或者2
 *parity:效验类型 取值为N,E,O,,S
 *出口参数：正确返回为1，错误返回为0
 *******************************************************************/
int uart_set(int fd, int speed, int flow_ctrl, int databits, int stopbits, int parity)
{
	int   i;
	int   status;
	int   speed_arr[] = { B115200, B19200, B9600, B4800, B2400, B1200, B300};
	int   name_arr[] = {115200,  19200,  9600,  4800,  2400,  1200,  300};

	struct termios options;

	/*tcgetattr(fd,&options)得到与fd指向对象的相关参数，并将它们保存于options,该函数还可以测试配置是否正确，该串口是否可用等。若调用成功，函数返回值为0，若调用失败，函数返回值为1.
	*/
	if ( tcgetattr( fd, &options)  !=  0)
	{
		perror("SetupSerial 1");
		return (FALSE);
	}

	//设置串口输入波特率和输出波特率
	for ( i = 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
	{
		if  (speed == name_arr[i])
		{
			cfsetispeed(&options, speed_arr[i]);
			cfsetospeed(&options, speed_arr[i]);
		}
	}

	//修改控制模式，保证程序不会占用串口
	options.c_cflag |= CLOCAL;
	//修改控制模式，使得能够从串口中读取输入数据
	options.c_cflag |= CREAD;

	//设置数据流控制
	switch (flow_ctrl)
	{

	case 0 ://不使用流控制
		options.c_cflag &= ~CRTSCTS;
		break;

	case 1 ://使用硬件流控制
		options.c_cflag |= CRTSCTS;
		break;
	case 2 ://使用软件流控制
		options.c_cflag |= IXON | IXOFF | IXANY;
		break;
	}
	//设置数据位
	//屏蔽其他标志位
	options.c_cflag &= ~CSIZE;
	switch (databits)
	{
	case 5    :
		options.c_cflag |= CS5;
		break;
	case 6    :
		options.c_cflag |= CS6;
		break;
	case 7    :
		options.c_cflag |= CS7;
		break;
	case 8:
		options.c_cflag |= CS8;
		break;
	default:
		fprintf(stderr, "Unsupported data size\n");
		return (FALSE);
	}
	//设置校验位
	switch (parity)
	{
	case 'n':
	case 'N': //无奇偶校验位。
		options.c_cflag &= ~PARENB;
		options.c_iflag &= ~INPCK;
		break;
	case 'o':
	case 'O'://设置为奇校验
		options.c_cflag |= (PARODD | PARENB);
		options.c_iflag |= INPCK;
		break;
	case 'e':
	case 'E'://设置为偶校验
		options.c_cflag |= PARENB;
		options.c_cflag &= ~PARODD;
		options.c_iflag |= INPCK;
		break;
	case 's':
	case 'S': //设置为空格
		options.c_cflag &= ~PARENB;
		options.c_cflag &= ~CSTOPB;
		break;
	default:
		fprintf(stderr, "Unsupported parity\n");
		return (FALSE);
	}
	// 设置停止位
	switch (stopbits)
	{
	case 1:
		options.c_cflag &= ~CSTOPB; break;
	case 2:
		options.c_cflag |= CSTOPB; break;
	default:
		fprintf(stderr, "Unsupported stop bits\n");
		return (FALSE);
	}

	//修改输出模式，原始数据输出
	options.c_oflag &= ~OPOST;

	options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	//options.c_lflag &= ~(ISIG | ICANON);

	//设置等待时间和最小接收字符
	options.c_cc[VTIME] = 1; /* 读取一个字符等待1*(1/10)s */
	options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */

	//如果发生数据溢出，接收数据，但是不再读取 刷新收到的数据但是不读
	tcflush(fd, TCIFLUSH);

	//激活配置 (将修改后的termios数据设置到串口中）
	if (tcsetattr(fd, TCSANOW, &options) != 0)
	{
		perror("com set error!\n");
		return (FALSE);
	}
	return (TRUE);
}

/*******************************************************************
 * 名称：UART0_Recv
 * 功能：接收串口数据
 * 入口参数：fd:文件描述符
 * rcv_buf:接收串口中数据存入rcv_buf缓冲区中
 * data_len:一帧数据的长度
 * 出口参数：正确返回为1，错误返回为0
 *******************************************************************/
int uart_recv(int fd, char *rcv_buf, int data_len)
{
	int len, fs_sel;
	fd_set fs_read;

	struct timeval time;

	FD_ZERO(&fs_read);
	FD_SET(fd, &fs_read);

	time.tv_sec = 10;
	time.tv_usec = 0;

	//使用select实现串口的多路通信
	// fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);
	// printf("fs_sel = %d\n", fs_sel);
	// if (fs_sel)
	{
		len = read(fd, rcv_buf, data_len);
		// printf("I am right!(version1.2) len = %d fs_sel = %d\n", len, fs_sel);
		return len;
	}
	// else
	// {
	// 	printf("Sorry,I am wrong!");
	// 	return FALSE;
	// }
}
/********************************************************************
 * 名称：uart_send
 * 功能：发送数据
 * 入口参数：fd:文件描述符
 * send_buf:存放串口发送数据
 * data_len:一帧数据的个数
 * 出口参数：正确返回为1，错误返回为0
 *******************************************************************/
int uart_send(int fd, char *send_buf, int data_len)
{
	int len = 0;

	len = write(fd, send_buf, data_len);
	if (len == data_len )
	{
		printf("send data is %s\n", send_buf);
		return len;
	}
	else
	{
		tcflush(fd, TCOFLUSH);
		return FALSE;
	}
}

static int tank_com = 0;
void turn_left(int vel)
{
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", -vel, vel);
	write(tank_com, cmd , strlen(cmd));
}

void turn_right(int vel)
{
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", vel, -vel);
	write(tank_com, cmd , strlen(cmd));
}

void forward(int vel)
{
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", vel, vel);
	write(tank_com, cmd , strlen(cmd));
}

void backward(int vel)
{
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", -vel, -vel);
	write(tank_com, cmd , strlen(cmd));
}
//x
//^
//|
//|
//y<----
//x 正方向运动
void linear_speed(int vel)
{
	if (vel > 1000)
	{
		vel = 1000;
	}

	if (vel < -1000)
	{
		vel = -1000;
	}
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", vel, vel);
	write(tank_com, cmd , strlen(cmd));
}
// theta 正方向,右手
void rotation_speed(int vel)
{
	if (vel > 1000)
	{
		vel = 1000;
	}

	if (vel < -1000)
	{
		vel = -1000;
	}
	char cmd[128] = {0};
	snprintf(cmd, 128, "!M %d %d\r", -vel, vel);
	write(tank_com, cmd , strlen(cmd));
}
int test_com(int argc, char **argv)
{
	int fd;                            //文件描述符
	int err;                           //返回调用函数的状态
	int len;
	int i;
	//char send_buf[20]="tiger john";
	char send_buf[20] = "tiger john";
	fd = uart_open("/dev/ttyUSB0"); //打开串口，返回文件描述符
	tank_com = fd;
	err = uart_set(fd, 38400, 0, 8, 1, 'N');
	int  vel = 0;
	char op = 0;
	char line[128];
	while (1) //循环读取数据
	{
		scanf("%c %d", &op, &vel);
		char get_buff;
		while (get_buff = getchar() != '\n' && get_buff != EOF);
		printf("cmd:%c %d\n", op, vel);
		switch (op)
		{
		case 'l':
			turn_left(vel);
			break;
		case 'r':
			turn_right(vel);
			break;
		case 'f':
			forward(vel);
			break;
		case 'b':
			backward(vel);
			break;
		default:
			break;
		}
	}
	uart_close(fd);
	exit (0);
}

void init_intelligent_car()
{
	int fd;                            //文件描述符
	int err;                           //返回调用函数的状态
	int len;
	int i;
	//char send_buf[20]="tiger john";
	char send_buf[20] = "tiger john";
	fd = uart_open("/dev/ttyUSB0"); //打开串口，返回文件描述符
	if (fd == FALSE)
	{
		getchar();
	}
	tank_com = fd;
	err = uart_set(fd, 115200, 0, 8, 1, 'N');

}


static int tank_fd = 0;
static int i = 0;
int get_gps(char gps_str[])
{
	int buff_len = 256;                         //返回调用函数的状态
	char rev_buff[buff_len] = {0};
	char single_char = 0;
	if (0 == tank_fd)
	{
		printf("Start Open COM!\n");
		getchar();
		tank_fd = uart_open_no_block("/dev/ttyUSB0"); //打开串口，返回文件描述符

		int err = uart_set(tank_fd, 38400, 0, 8, 1, 'N');
		if (0 == err)
		{
			printf("Open COM Failed !\n");
			getchar();
			return 0;
		}
		return 0;
	}
	printf("Start receive COM\n");
	while (1)
	{
		if (uart_recv(tank_fd, &single_char, 1) > 0)
		{
			// printf("none block well done\n");
			if (single_char == '\n')
			{
				rev_buff[i] = '\0';
				i = 0;
				memcpy(gps_str, rev_buff, strlen(rev_buff));
				gps_str[strlen(gps_str)] = '\0';
				printf("%s\n", rev_buff);
				return 1;
			}
			else
			{
				rev_buff[i] = single_char;
				i++;
				printf("%c\n", single_char);
				return 0;
			}
		}
	}
	uart_close(tank_fd);
	exit(0);
}
